#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cassert>
#include<string>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<pthread.h>
#include<unistd.h>
using namespace std;

struct sockaddr_in server;

static void Usage(string name)
{
    cout << "Usage:\n\t" << name << " server_ip server_port" << endl;
}

void* recverAndPrint(void* args)
{
    while(true)
    {
        int sockfd = *(int*)args;
        char buffer[1024];

        struct sockaddr_in temp;
        socklen_t len = sizeof(temp);

        ssize_t s = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&temp, &len);
        if(s > 0)
        {
            buffer[s] = '\0';
            cout << "server echo# " << buffer << endl;
        }
    }
}

// ./udpClient server_ip server_port
// 如果一个客户端要连接server,必须要知道server对应的ip和port
int main(int argc, char* argv[])
{
    if(argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }

    //1.根据命令行,设置要访问的服务器ip
    string server_ip = argv[1];
    uint16_t server_port = atoi(argv[2]);

    //2.创建客户端
    //2.1创建socket
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    assert(sockfd > 0);

    //client需要自己bind吗?不需要,OS自动给你bind,所谓的不需要指的是不需要用户自己bind端口信息,因为OS会自动给你绑定
    //如果我非要自己bind呢? 可以,但极不推荐
    //所有的客户端软件<->服务器通信的时候,必须得有client[ip:port] <-> server[ip:port]
    //为什么呢?client很多,不能给客户端bind指定的port,port可能被别的client使用了,你的client就无法启动了
    //那么server凭什么要bind呢?因为server提供的服务,必须被所有人知道,server不能随便改变

    //2.2填写服务器对应的信息
    memset(&server, 0, sizeof server);

    server.sin_family = AF_INET;
    server.sin_port = htons(server_port);
    server.sin_addr.s_addr = inet_addr(server_ip.c_str());

    pthread_t t;
    pthread_create(&t, nullptr, recverAndPrint, (void*)&sockfd);

    //3.通信过程
    string buffer;
    while(true)
    {
        cerr << "Please Enter# ";
        getline(cin, buffer);
        //发送消息给server
        sendto(sockfd, buffer.c_str(), buffer.size(), 0, (const sockaddr*)&server, sizeof(server));
        //首次调用sendto()的时候,我们的client会自动bind自己的ip和port
    }

    close(sockfd);


    return 0;
}